from collections import namedtuple

### RESERVED WORDS ###
reserved = {
	'while': 'TOKEN020',
	'skip': 'TOKEN016',
	'true': 'TOKEN007',
	':=': 'TOKEN015',
	'then': 'TOKEN018',
	'false': 'TOKEN008',
	'}': 'TOKEN023',
	'else': 'TOKEN019',
	'-': 'TOKEN006',
	'{': 'TOKEN022',
	';': 'TOKEN013',
	')': 'TOKEN003',
	'<=': 'TOKEN010',
	'(': 'TOKEN002',
	'if': 'TOKEN017',
	'do': 'TOKEN021',
	'!': 'TOKEN011',
	'*': 'TOKEN004',
	'=': 'TOKEN009',
	'&': 'TOKEN012',
	'+': 'TOKEN005'
	}

### TOKENS ###
tokens = (
	'TOKEN014',
	'TOKEN008',
	'TOKEN013',
	'TOKEN010',
	'TOKEN011',
	'TOKEN002',
	'TOKEN021',
	'TOKEN004',
	'TOKEN009',
	'TOKEN016',
	'TOKEN001',
	'TOKEN022',
	'TOKEN019',
	'TOKEN015',
	'TOKEN012',
	'TOKEN020',
	'TOKEN007',
	'TOKEN018',
	'TOKEN006',
	'TOKEN003',
	'TOKEN017',
	'TOKEN023',
	'TOKEN005'
	)

def t_TOKEN001(t):
	r'[0-9]+'
	t.type = reserved.get(t.value, 'TOKEN001')
	return t
t_TOKEN002 = r'\('
t_TOKEN003 = r'\)'
t_TOKEN004 = r'\*'
t_TOKEN005 = r'\+'
t_TOKEN006 = r'-'
t_TOKEN007 = r'true'
t_TOKEN008 = r'false'
t_TOKEN009 = r'='
t_TOKEN010 = r'<='
t_TOKEN011 = r'!'
t_TOKEN012 = r'&'
t_TOKEN013 = r';'
def t_TOKEN014(t):
	r'[a-zA-Z]\w*'
	t.type = reserved.get(t.value, 'TOKEN014')
	return t
t_TOKEN015 = r':='
t_TOKEN016 = r'skip'
t_TOKEN017 = r'if'
t_TOKEN018 = r'then'
t_TOKEN019 = r'else'
t_TOKEN020 = r'while'
t_TOKEN021 = r'do'
t_TOKEN022 = r'\{'
t_TOKEN023 = r'\}'
t_ignore_TOKEN024 = r'\/\/[^\n]*\n'

def t_error(t):
	print("Illegal character '%s'" % t.value[0])
	t.lexer.skip(1)

### PRECEDENCE ###
precedence = (
    ('left', 'TOKEN012'), 
    ('nonassoc', 'TOKEN009', 'TOKEN010'), 
    ('left', 'TOKEN005', 'TOKEN006'), 
    ('left', 'TOKEN004'), 
    ('right', 'TOKEN011'),
    )
start = 'main'

### RULES ###
def __concat__(x,y):
	return (x if type( x ) is list else [x]) + (y if type( y ) is list else [y])

def p_RULE001(p): # aexp : /[0-9]+/ > (Numeral n=$1)
	"aexp : TOKEN001"
	p[0] = Numeral(n=p[1])

def p_RULE002(p): # aexp : '(' aexp ')' > $2
	"aexp : TOKEN002 aexp TOKEN003"
	p[0] = p[2]

def p_RULE003(p): # aexp : aexp '*' aexp > (Multiplication a1=$1 a2=$3)
	"aexp : aexp TOKEN004 aexp"
	p[0] = Multiplication(a1=p[1], a2=p[3])

def p_RULE004(p): # aexp : aexp '+' aexp > (Addition a1=$1 a2=$3)
	"aexp : aexp TOKEN005 aexp"
	p[0] = Addition(a1=p[1], a2=p[3])

def p_RULE005(p): # aexp : aexp '-' aexp > (Subtraction a1=$1 a2=$3)
	"aexp : aexp TOKEN006 aexp"
	p[0] = Subtraction(a1=p[1], a2=p[3])

def p_RULE006(p): # bexp : 'true' > (LiteralTrue)
	"bexp : TOKEN007"
	p[0] = LiteralTrue()

def p_RULE007(p): # bexp : 'false' > (LiteralFalse)
	"bexp : TOKEN008"
	p[0] = LiteralFalse()

def p_RULE008(p): # bexp : '(' bexp ')' > $2
	"bexp : TOKEN002 bexp TOKEN003"
	p[0] = p[2]

def p_RULE009(p): # bexp : aexp '=' aexp > (Equals a1=$1 a2=$3)
	"bexp : aexp TOKEN009 aexp"
	p[0] = Equals(a1=p[1], a2=p[3])

def p_RULE010(p): # bexp : aexp '<=' aexp > (LessThanOrEqual a1=$1 a2=$3)
	"bexp : aexp TOKEN010 aexp"
	p[0] = LessThanOrEqual(a1=p[1], a2=p[3])

def p_RULE011(p): # bexp : '!' bexp > (Negation b=$2)
	"bexp : TOKEN011 bexp"
	p[0] = Negation(b=p[2])

def p_RULE012(p): # bexp : bexp '&' bexp > (Conjuntion b1=$1 b2=$3)
	"bexp : bexp TOKEN012 bexp"
	p[0] = Conjuntion(b1=p[1], b2=p[3])

def p_RULE013(p): # main : seq > (Sequence stmts=$1)
	"main : seq"
	p[0] = Sequence(stmts=p[1])

def p_RULE014(p): # seq : stmt > [$1]
	"seq : stmt"
	p[0] = [p[1]]

def p_RULE015(p): # seq : seq ';' stmt > $1:$3
	"seq : seq TOKEN013 stmt"
	p[0] = __concat__(p[1], p[3])

def p_RULE016(p): # stmt : /[a-zA-Z]\w*/ ':=' aexp > (Assignment x=$1 a=$2)
	"stmt : TOKEN014 TOKEN015 aexp"
	p[0] = Assignment(x=p[1], a=p[2])

def p_RULE017(p): # stmt : 'skip' > (Skip)
	"stmt : TOKEN016"
	p[0] = Skip()

def p_RULE018(p): # stmt : 'if' bexp 'then' stmt 'else' stmt > (IfThenElse b=$2 s1=$4 s2=$6)
	"stmt : TOKEN017 bexp TOKEN018 stmt TOKEN019 stmt"
	p[0] = IfThenElse(b=p[2], s1=p[4], s2=p[6])

def p_RULE019(p): # stmt : 'while' bexp 'do' stmt > (WhileDo b=$2 s=$4)
	"stmt : TOKEN020 bexp TOKEN021 stmt"
	p[0] = WhileDo(b=p[2], s=p[4])

def p_RULE020(p): # stmt : '{' seq '}' > (Sequence stmts=$2)
	"stmt : TOKEN022 seq TOKEN023"
	p[0] = Sequence(stmts=p[2])

def p_error(t):
	if hasattr(t, 'value'):
		print("Syntax error at '%s'" % t.value)
	else:
		print ("Syntex error")

### AST ###
class ArithmeticExpression:
	pass

class Numeral(namedtuple('Numeral', 'n'), ArithmeticExpression):
	pass

class Multiplication(namedtuple('Multiplication', 'a1 a2'), ArithmeticExpression):
	pass

class Addition(namedtuple('Addition', 'a1 a2'), ArithmeticExpression):
	pass

class Subtraction(namedtuple('Subtraction', 'a1 a2'), ArithmeticExpression):
	pass

class BooleanExpression:
	pass

class LiteralTrue(namedtuple('LiteralTrue', ''), BooleanExpression):
	pass

class LiteralFalse(namedtuple('LiteralFalse', ''), BooleanExpression):
	pass

class Equals(namedtuple('Equals', 'a1 a2'), BooleanExpression):
	pass

class LessThanOrEqual(namedtuple('LessThanOrEqual', 'a1 a2'), BooleanExpression):
	pass

class Negation(namedtuple('Negation', 'b'), BooleanExpression):
	pass

class Conjuntion(namedtuple('Conjuntion', 'b1 b2'), BooleanExpression):
	pass

class Statement:
	pass

class Sequence(namedtuple('Sequence', 'stmts'), Statement):
	pass

class Assignment(namedtuple('Assignment', 'x a'), Statement):
	pass

class Skip(namedtuple('Skip', ''), Statement):
	pass

class IfThenElse(namedtuple('IfThenElse', 'b s1 s2'), Statement):
	pass

class WhileDo(namedtuple('WhileDo', 'b s'), Statement):
	pass


#Build the parser
import ply.lex as lex
lex.lex()
import ply.yacc as yacc
yacc.yacc()